﻿#include "pch.h"
#include "sbcs.h"
#include <fcntl.h>
#if _SB_UNIX_
#include <sys/types.h>
#include <sys/stat.h>
#endif

// MSC v13.10 - ctype.h
#define _k_iswdigit(c)                 (L'0' <= (c) && (c) <= L'9')


//////////////////////////////////////////////////////////////////////////
// 파일

// 파일 구조체
struct kFile
{
	char*				name;
	kFileAccess			acs;
#if _SB_WINDOWS_
	kuintptr			fd;
#else
	kint				fd;
#endif

};

// 최대 할당 크기
static ksize_t k_impl_file_max_alloc_size = 512 * 1024 * 1024;

// 분석
void k_file_access_parse(kFileAccess* self, const char* mode)
{
#if _SB_WINDOWS_
	char ch;

	self->mode = 0;

	if (mode)
	{
		self->access = 0;
		self->share = FILE_SHARE_READ;
		self->attr = FILE_ATTRIBUTE_NORMAL;

		for (; *mode; ++mode)
		{
			switch (*mode)
			{
				case 'a':
					self->mode = OPEN_ALWAYS;
					self->access = GENERIC_WRITE;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case 'r':
					self->mode = OPEN_EXISTING;
					self->access = GENERIC_READ;
					self->flag = KFF_READ | KFF_SEEK;
					break;

				case 'w':
					self->mode = CREATE_ALWAYS;
					self->access = GENERIC_WRITE;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case '+':
					self->access = GENERIC_WRITE | GENERIC_READ;
					self->flag = KFF_READ | KFF_WRITE | KFF_SEEK;
					break;

				case '@':
					ch = *(mode + 1);

					if (ch == 'R')
						self->share = FILE_SHARE_READ, ++mode;
					else if (ch == 'W')
						self->share = FILE_SHARE_WRITE, ++mode;
					else if (ch == '+')
						self->share = FILE_SHARE_READ | FILE_SHARE_WRITE, ++mode;

					break;

				case '!':
					if (self->mode == CREATE_ALWAYS)
						self->attr |= FILE_FLAG_DELETE_ON_CLOSE;

					break;
			}
		}
	}

	if (self->mode == 0)
	{
		self->flag = KFF_READ | KFF_SEEK;
		self->mode = OPEN_EXISTING;
		self->access = GENERIC_READ;
		self->attr = FILE_ATTRIBUTE_NORMAL;
		self->share = FILE_SHARE_READ;
	}
#else
	char ch, cm;

	self->mode = 0;
	self->access = 0;

	if (mode)
	{
		for (; *mode; ++mode)
		{
			switch (*mode)
			{
				case 'a':
					self->mode = O_WRONLY | O_APPEND | O_CREAT;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case 'r':
					self->mode = O_RDONLY;
					self->flag = KFF_READ | KFF_SEEK;
					break;

				case 'w':
					self->mode = O_WRONLY | O_TRUNC | O_CREAT;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case '+':
					self->mode |= O_RDWR;
					self->flag = KFF_READ | KFF_WRITE | KFF_SEEK;
					break;

				case '@':
					cm = *(mode + 1);

					if (cm != '\0')
					{
						if (cm == 'U')
						{
							ch = *(mode + 2);

							if (ch != '\0')
							{
								if (ch == 'R')
									self->access |= S_IRUSR;
								else if (ch == 'W')
									self->access |= S_IWUSR;
								else if (ch == 'X')
									self->access |= S_IXUSR;

								mode++;
							}

							mode++;
						}
						else if (cm == 'G')
						{
							ch = *(mode + 2);

							if (ch != '\0')
							{
								if (ch == 'R')
									self->access |= S_IRGRP;
								else if (ch == 'W')
									self->access |= S_IWGRP;
								else if (ch == 'X')
									self->access |= S_IXGRP;

								mode++;
							}

							mode++;
						}
						else if (cm == 'O')
						{
							ch = *(mode + 2);

							if (ch != '\0')
							{
								if (ch == 'R')
									self->access |= S_IROTH;
								else if (ch == 'W')
									self->access |= S_IWOTH;
								else if (ch == 'X')
									self->access |= S_IXOTH;

								mode++;
							}

							mode++;
						}
						else if (k_isdigit(cm))
						{
							const char* p = mode + 2;

							while (k_isdigit(*p))
								p++;

							if ((p - (mode + 1)) < 63)
							{
								char sz[64];
								k_strncpy(sz, (mode + 1), p - (mode + 1));
								self->access = k_atoi_o(sz);
								k_mesgf("mode: %s (%d)\n", sz, self->access);
							}

							mode = p;
						}
					}

					break;

				case '!':
					break;
			}
		}
	}

	if (self->mode == 0)
	{
		self->mode = O_RDONLY;
		self->flag = KFF_READ | KFF_SEEK;
	}

	if (self->access == 0 && (self->mode & O_CREAT) != 0)
		self->access = (S_IRUSR | S_IWUSR) | (S_IRGRP | S_IWGRP) | (S_IROTH);
#endif
}

void k_file_access_parse_l(kFileAccess* self, const kwchar* mode)
{
#if _SB_WINDOWS_
	kwchar ch;

	self->mode = 0;

	if (mode)
	{
		self->access = 0;
		self->share = FILE_SHARE_READ;
		self->attr = FILE_ATTRIBUTE_NORMAL;

		for (; *mode; ++mode)
		{
			switch (*mode)
			{
				case L'a':
					self->mode = OPEN_ALWAYS;
					self->access = GENERIC_WRITE;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case L'r':
					self->mode = OPEN_EXISTING;
					self->access = GENERIC_READ;
					self->flag = KFF_READ | KFF_SEEK;
					break;

				case L'w':
					self->mode = CREATE_ALWAYS;
					self->access = GENERIC_WRITE;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case L'+':
					self->access = GENERIC_WRITE | GENERIC_READ;
					self->flag = KFF_READ | KFF_WRITE | KFF_SEEK;
					break;

				case L'@':
					ch = *(mode + 1);

					if (ch == L'R')
						self->share = FILE_SHARE_READ, ++mode;
					else if (ch == L'W')
						self->share = FILE_SHARE_WRITE, ++mode;
					else if (ch == L'+')
						self->share = FILE_SHARE_READ | FILE_SHARE_WRITE, ++mode;

					break;

				case L'!':
					if (self->mode == CREATE_ALWAYS)
						self->attr |= FILE_FLAG_DELETE_ON_CLOSE;

					break;
			}
		}
	}

	if (self->mode == 0)
	{
		self->flag = KFF_READ | KFF_SEEK;
		self->mode = OPEN_EXISTING;
		self->access = GENERIC_READ;
		self->attr = FILE_ATTRIBUTE_NORMAL;
		self->share = FILE_SHARE_READ;
	}
#else
	kwchar ch, cm;

	self->mode = 0;
	self->access = 0;

	if (mode)
	{
		for (; *mode; ++mode)
		{
			switch (*mode)
			{
				case L'a':
					self->mode = O_WRONLY | O_APPEND | O_CREAT;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case L'r':
					self->mode = O_RDONLY;
					self->flag = KFF_READ | KFF_SEEK;
					break;

				case L'w':
					self->mode = O_WRONLY | O_TRUNC | O_CREAT;
					self->flag = KFF_WRITE | KFF_SEEK;
					break;

				case L'+':
					self->mode |= O_RDWR;
					self->flag = KFF_READ | KFF_WRITE | KFF_SEEK;
					break;

				case L'@':
					cm = *(mode + 1);

					if (cm != L'\0')
					{
						if (cm == L'U')
						{
							ch = *(mode + 2);

							if (ch != L'\0')
							{
								if (ch == L'R')
									self->access |= S_IRUSR;
								else if (ch == L'W')
									self->access |= S_IWUSR;
								else if (ch == L'X')
									self->access |= S_IXUSR;

								mode++;
							}

							mode++;
						}
						else if (cm == L'G')
						{
							ch = *(mode + 2);

							if (ch != L'\0')
							{
								if (ch == L'R')
									self->access |= S_IRGRP;
								else if (ch == L'W')
									self->access |= S_IWGRP;
								else if (ch == L'X')
									self->access |= S_IXGRP;

								mode++;
							}

							mode++;
						}
						else if (cm == L'O')
						{
							ch = *(mode + 2);

							if (ch != L'\0')
							{
								if (ch == L'R')
									self->access |= S_IROTH;
								else if (ch == L'W')
									self->access |= S_IWOTH;
								else if (ch == L'X')
									self->access |= S_IXOTH;

								mode++;
							}

							mode++;
						}
						else if (_k_iswdigit(cm))
						{
							const kwchar* p = mode + 2;

							while (_k_iswdigit(*p))
								p++;

							if ((p - (mode + 1)) < 63)
							{
								kwchar sz[64];
								k_wcsncpy(sz, (mode + 1), p - (mode + 1));
								self->access = k_wtoi_o(sz);
								k_mesgf("mode: %s (%d)\n", sz, self->access);
							}

							mode = p;
						}
					}

					break;

				case L'!':
					break;
			}
		}
	}

	if (self->mode == 0)
	{
		self->mode = O_RDONLY;
		self->flag = KFF_READ | KFF_SEEK;
	}

	if (self->access == 0 && (self->mode & O_CREAT) != 0)
		self->access = (S_IRUSR | S_IWUSR) | (S_IRGRP) | (S_IROTH);
#endif
}

/**
 * 새 파일 처리 구조를 만든다.
 * @param	filename	파일의 이름.
 * @param	mode		파일 처리 모드.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kFile*.
 */
kFile* k_file_new(const char* filename, const char* mode)
{
	kFile* self;
#if _SB_WINDOWS_
	kwchar uni[K_MAX_PATH];
#if !_SB_WINDOWS_DESKTOP_
	CREATEFILE2_EXTENDED_PARAMETERS cep;
#endif
#endif

	k_return_value_if_fail(filename, NULL);

	self = k_new_1(kFile);

	k_file_access_parse(&self->acs, mode);

#if _SB_WINDOWS_
	k_conv_utf8_to_uni(uni, K_MAX_PATH - 1, filename, 0);

#if _SB_WINDOWS_DESKTOP_
	self->fd = (kuintptr)CreateFile(uni, self->acs.access, self->acs.share, NULL, self->acs.mode, self->acs.attr, NULL);
#else
	k_zero_1(&cep);
	cep.dwFileAttributes = self->acs.attr;
	self->fd = (kuintptr)CreateFile2(uni, self->acs.access, self->acs.share, self->acs.mode, &cep);
#endif

	if (!self->fd || self->fd == (kuintptr)INVALID_HANDLE_VALUE)
#else
	self->fd = self->acs.access == 0 ? open(filename, self->acs.mode) : open(filename, self->acs.mode, self->acs.access);

	if (self->fd < 0)
#endif
	{
		k_delete(self);
		return NULL;
	}

	self->name = k_strdup(filename);

	return self;
}

/**
 * 유니코드용 새 파일 구조를 만든다.
 * @param	filename	파일의 이름.
 * @param	mode		파일 처리 모드.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kFile*.
 */
kFile* k_file_new_l(const kwchar* filename, const kwchar* mode)
{
	kFile* self;
	char asc[K_MAX_PATH];
#if _SB_WINDOWS_ && !_SB_WINDOWS_DESKTOP_
	CREATEFILE2_EXTENDED_PARAMETERS cep;
#endif

	k_return_value_if_fail(filename, NULL);

	self = k_new_1(kFile);

	k_file_access_parse_l(&self->acs, mode);

	//k_conv_uni_to_asc(asc, K_MAX_PATH - 1, filename, 0);
	k_conv_uni_to_utf8(asc, K_MAX_PATH - 1, filename, 0);

#if _SB_WINDOWS_
#if _SB_WINDOWS_DESKTOP_
	self->fd = (kuintptr)CreateFile(filename, self->acs.access, self->acs.share, NULL, self->acs.mode, self->acs.attr, NULL);
#else
	k_zero_1(&cep);
	cep.dwFileAttributes = self->acs.attr;
	self->fd = (kuintptr)CreateFile2(filename, self->acs.access, self->acs.share, self->acs.mode, &cep);
#endif

	if (!self->fd || self->fd == (kuintptr)INVALID_HANDLE_VALUE)
#else
	self->fd = self->acs.access == 0 ? open(asc, self->acs.mode) : open(asc, self->acs.mode, self->acs.access);

	if (self->fd < 0)
#endif
	{
		k_delete(self);
		return NULL;
	}

	self->name = k_strdup(asc);

	return self;
}

/**
 * 파일 복제. 핸들을 복제하여 따로 사용할 수 있도록 한다.
 * @param [입력,반환]	src	(널값이 아닌) 원본.
 * @return	문제가 있거나 실패하면 널값을 반환, 성공할 때 반환값은 kFile*.
 */
kFile* k_file_new_dup(kFile* src)
{
	kFile* self = k_new_1(kFile);
#if _SB_WINDOWS_
	HANDLE h, p, d;
#endif

	memcpy(self, src, sizeof(kFile));

#if _SB_WINDOWS_
	h = (HANDLE)self->fd;
	p = GetCurrentProcess();

	if (!DuplicateHandle(p, h, p, &d, 0, FALSE, DUPLICATE_SAME_ACCESS))
	{
		k_delete(self);

		return NULL;
	}

	self->fd = (kuintptr)d;
#else
	self->fd = dup(src->fd);

	if (self->fd < 0)
	{
		// 허걱!!!
		k_delete(self);

		return NULL;
	}
#endif

	self->name = k_strdup(src->name);

	return self;
}

/**
 * 파일 구조를 제거한다. 더 이상 파일 관리를 하지 않을 때 사용한다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 */
void k_file_delete(kFile* self)
{
#if _SB_WINDOWS_
	if (self->fd && self->fd != (kuintptr)INVALID_HANDLE_VALUE)
		CloseHandle((HANDLE)self->fd);
#else
	if (self->fd >= 0)
		close(self->fd);
#endif

	k_delete(self->name);
	k_delete(self);
}

/**
 * 파일 플래그를 가져온다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	mask		플래그의 마스크.
 * @return	파일 플래그.
 */
kint k_file_flags(kFile* self, kint mask)
{
	return K_OMASK(self->acs.flag, mask);
}

/**
 * 파일 이름을 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	파일의 이름.
 */
const char* k_file_name(kFile* self)
{
	return self->name;
}

/**
 * 파일에서 읽는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	읽은 데이터를 저장할 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		읽을 크기.
 * @return	실제 읽은 길이를 반환.
 */
kint k_file_read(kFile* self, kpointer buffer, int offset, kint size)
{
	kbyte* ptr;
#if _SB_WINDOWS_
	int ret;
#endif

	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(size >= 0, 0);

	ptr = (kbyte*)buffer;

#if _SB_WINDOWS_
	return ReadFile((HANDLE)self->fd, ptr + offset, size, (LPDWORD)&ret, NULL) ? ret : 0;
#else
	return read(self->fd, ptr + offset, size);
#endif
}

/**
 * 파일에 쓴다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	buffer  	쓸 데이터가 들어 있는 버퍼.
 * @param	offset  	버퍼의 옵셋.
 * @param	size		쓸 크기.
 * @return	실제 쓴 길이를 반환.
 */
kint k_file_write(kFile* self, kconstpointer buffer, int offset, kint size)
{
	const kbyte* ptr;
#if _SB_WINDOWS_
	int ret;
#endif

	k_return_value_if_fail(buffer != NULL, -1);
	k_return_value_if_fail(size >= 0, 0);

	ptr = (const kbyte*)buffer;

#if _SB_WINDOWS_
	return WriteFile((HANDLE)self->fd, ptr + offset, size, (LPDWORD)&ret, NULL) ? ret : 0;
#else
	return write(self->fd, ptr + offset, size);
#endif
}

/**
 * 파일 크기를 얻는다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	파일의 길이.
 */
klong k_file_size(kFile* self)
{
#if _SB_WINDOWS_
#if _SB_WINDOWS_DESKTOP_
	LARGE_INTEGER ll;
	return GetFileSizeEx((HANDLE)self->fd, &ll) ? ll.QuadPart : -1;
#else
	FILE_STANDARD_INFO fi = {0};

	if (!GetFileInformationByHandleEx((HANDLE)self->fd, FileStandardInfo, &fi, sizeof(FILE_STANDARD_INFO)))
		return 0;

	return fi.EndOfFile.QuadPart;
#endif
#else
#if _SB_LINUX_
	off_t save, last;
#elif _SB_BSD_
	int save, last;
#else
	long save, last;
#endif
	save = lseek(self->fd, 0, SEEK_CUR);
	last = lseek(self->fd, 0, SEEK_END);
	lseek(self->fd, save, SEEK_SET);
	return last;
#endif
}

/**
 * 파일 위치 얻기.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	현재 파일의 읽고 쓰는 위치.
 */
klong k_file_tell(kFile* self)
{
#if _SB_WINDOWS_
	LARGE_INTEGER ll, pl;
	pl.QuadPart = 0;
	return SetFilePointerEx((HANDLE)self->fd, pl, &ll, FILE_CURRENT) ? ll.QuadPart : -1;
#else
	return lseek(self->fd, 0, SEEK_CUR);
#endif
}

/**
 * 파일 위치 변경.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	offset  	이동할 위치.
 * @param	org			방식 (C형식 SEEK와 동일).
 * @return	변경된 위치.
 */
klong k_file_seek(kFile* self, klong offset, int org)
{
#if _SB_WINDOWS_
	LARGE_INTEGER ll;
	LARGE_INTEGER* po = (LARGE_INTEGER*)&offset;

	BOOL b = SetFilePointerEx((HANDLE)self->fd, *po, &ll, org);

	return b ? ll.QuadPart : -1;
#else
	return lseek(self->fd, offset, org);
#endif
}

/**
 * 파일 갱신. 파일 내용을 갱신한다. 쓰기 버퍼의 남은 데이터를 모두 쓴다.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_file_flush(kFile* self)
{
#if _SB_WINDOWS_
	return FlushFileBuffers((HANDLE)self->fd) != 0;
#else
#if 0
	sync();
#endif
	return TRUE;
#endif
}

/**
 * 파일에 쓰기. 포맷 방식.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	fmt			포맷 문자열.
 * @return	실제 쓴 길이.
 */
kint k_file_printf(kFile* self, const char* fmt, ...)
{
	char* buf;
	kint len;
	va_list va;

	va_start(va, fmt);
	len = k_vsnprintf(NULL, 0, fmt, va);
	va_end(va);

	buf = k_new(len + 1, char);
	va_start(va, fmt);
	k_vsnprintf(buf, len + 1, fmt, va);
	va_end(va);

	len = k_file_write(self, buf, 0, len);

	k_delete(buf);

	return len;
}

/**
 * 파일에 쓰기, 포맷 방식.
 * @param [입력]	self	개체나 인터페이스의 자기 자신 값.
 * @param	fmt			포맷 문자열.
 * @param	va			가변 인수.
 * @return	실제 쓴 길이.
 */
kint k_file_vprintf(kFile* self, const char* fmt, va_list va)
{
	char* buf;
	kint len;

	len = k_vsnprintf(NULL, 0, fmt, va);

	buf = k_new(len + 1, char);
	k_vsnprintf(buf, len + 1, fmt, va);

	len = k_file_write(self, buf, 0, len);

	k_delete(buf);

	return len;
}

/**
 * 파일이 있나 조사한다.
 * @param	filename	파일의 이름.
 * @param [반환]	res 	(널값이 아니면) 파일 처리 플래그로 KFAS_로 시작하는 마스크 플래그.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_file_exist(const char* filename, kint* res)
{
#if _SB_WINDOWS_
	WIN32_FIND_DATA ffd;
	HANDLE h;
	kwchar uni[K_MAX_PATH];

	if (!filename)
		return FALSE;

	k_conv_utf8_to_utf16(uni, K_MAX_PATH - 1, filename, 0);

	h = FindFirstFileEx(uni, FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);

	if (h == INVALID_HANDLE_VALUE)
		return FALSE;
	else
	{
		FindClose(h);

		if (res != NULL && ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			*res = KFAS_DIR;

		return TRUE;
	}
#else
	struct stat s;
	kint n;

	n = stat(filename, &s);

	if (n < 0)
		return FALSE;
	else
	{
		if (res != NULL && (s.st_mode & 0x4000) != 0)
			*res = KFAS_DIR;

		return TRUE;
	}
#endif
}

/**
 * 파일이 있나 조사한다. 유니코드 버전.
 * @param	filename	파일의 이름.
 * @param [반환]	res 	(널값이 아니면) 파일 처리 플래그로 KFAS_로 시작하는 마스크 플래그.
 * @return	성공하면 참, 실패하면 거짓.
 */
kcham k_file_exist_l(const kwchar* filename, kint* res)
{
#if _SB_WINDOWS_
	WIN32_FIND_DATA ffd;
	HANDLE h;

	if (!filename)
		return FALSE;

	h = FindFirstFileEx(filename, FindExInfoStandard, &ffd, FindExSearchNameMatch, NULL, 0);

	if (h == INVALID_HANDLE_VALUE)
		return FALSE;
	else
	{
		FindClose(h);

		if (res != NULL && ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			*res = KFAS_DIR;

		return TRUE;
	}
#else
	struct stat s;
	char asc[260];
	kint n;

	k_conv_uni_to_utf8(asc, 260 - 1, filename, 0);
	n = stat(asc, &s);

	if (n < 0)
		return FALSE;
	else
	{
		if (res != NULL && (s.st_mode & 0x4000) != 0)
			*res = KFAS_DIR;

		return TRUE;
	}
#endif
}

/**
 * k_file_alloc 함수에서 사용하는 파일 읽기 최대 할당 크기.
 * @return	최대 할당 크기.
 */
ksize_t k_file_get_max_alloc_size(void)
{
	return k_impl_file_max_alloc_size;
}

/**
 * k_file_alloc 함수에서 사용할 파일 읽기 최대 할당 크기 설정.
 * @param	n	할당할 크기.
 */
void k_file_set_max_alloc_size(ksize_t n)
{
	k_impl_file_max_alloc_size = n == 0 ? (512 * 1024 * 1024) : n;
}

/**
 * 파일 할당. 즉 파일 전체를 읽어 메모리에 할당한 후 반환한다.
 * @param	filename	파일의 이름.
 * @param [반환]	size	(널값이 아니면) 읽은 파일의 크기.
 * @return	읽은 버퍼. 사용한 다음 k_free 함수로 해제해야한다.
 */
kpointer k_file_alloc(const char* filename, kint* size)
{
	kFile* file;
	kpointer buf;
	kulong len;

	k_return_value_if_fail(filename != NULL, NULL);

	file = k_file_new(filename, "rb");

	if (!file)
		return NULL;

	len = k_file_size(file);

	if (!len || len > k_impl_file_max_alloc_size)
	{
		k_file_delete(file);
		return NULL;
	}

	buf = k_new((ksize_t)len + 4, kbyte);
	k_file_read(file, buf, 0, (kint)len);

	k_file_delete(file);

	if (size)
		*size = (kint)len;

	return buf;
}

/**
 * 파일 할당. 즉 파일 전체를 읽어 메모리에 할당한 후 반환한다. 유니코드 버전.
 * @param	filename	파일의 이름.
 * @param [반환]	size	(널값이 아니면) 읽은 파일의 크기.
 * @return	읽은 버퍼. 사용한 다음 k_free 함수로 해제해야한다.
 */
kpointer k_file_alloc_l(const kwchar* filename, kint* size)
{
	kFile* file;
	kpointer buf;
	kulong len;

	k_return_value_if_fail(filename != NULL, NULL);

	file = k_file_new_l(filename, L"rb");

	if (!file)
		return NULL;

	len = k_file_size(file);

	if (!len || len > k_impl_file_max_alloc_size)
	{
		k_file_delete(file);
		return NULL;
	}

	buf = k_new((ksize_t)len + 4, kbyte);
	k_file_read(file, buf, 0, (kint)len);

	k_file_delete(file);

	if (size)
		*size = (kint)len;

	return buf;
}
